﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.CompilerServices;
using System.IO;

namespace Protocolo
{
    public partial class formProtocolo : Form
    {
        public formProtocolo()
        {
            InitializeComponent();
        }

        #region Variáveis

        int iRetorno = 0;//Variável para retorno das chamadas da DLL MP2032
        int iNumeroVias = 2;//Numero de vias de que serão autenticadas.
        int iContadorViasAutenticadas = 1;//Número de vias que foram autenticadas dentro do ciclo de autenticação
        int iTotalAutenticacao = 1;//Total de documentos autenticados pela impressora.

        readonly int TIPOIMPRESSORA = 1;//CONSTANTE definido pelo fabricante e representa a impressora MP 20 MI - NÃO MODIFICAR
        readonly int COLUNAIMPRESSAO = 48;//LARGURA da coluna de impressão  da impressora em caracteres.
        readonly int CARACTERUSADOSISTEMA = 27;//número de caracteres da coluna de impressão já utilizados.
        readonly int MAXIMOAUTENTICACAO = 99999;//máximo de documentos que serão autenticados pela impressora. Após esse valor, o contador é reiniciado.                
        readonly int LINHACONFIGURACAO = 3;//Quantidade de linhas com informações para a configuração da impressora que serão provenientes do arquivo config.txt
        readonly int CARACTERMINIMOSETOR = 3;//quantidade mínima de caracteres para definir o setor.
        
        readonly string CAMINHOARQUIVOCONFIGURACAO = Path.Combine(Application.StartupPath, "config.txt");//path com o caminho completo da localização do arquivo config.txt. Ele será definido baseado na localização do executável.

        string sPorta;//porta utilizada pela impressora. Informação proveniente da linha[0] do arquivo config.txt
        string sSetor;//setor que irá recibar os documentos. Essa informação vem da linha[1] do arquivo config.txt
        string sAutenticacao;//string final com o texto que será impresso na autenticação
        

        #endregion

        /*
         * Eventos acionado no carregamento do formulário.
         */
        private void formProtocolo_Load(object sender, EventArgs e)
        {
            this.inicializaImpressora();
        }

        #region Inicialização da Impressora

        /*
         * Método para validação do arquivo de configuração, definição do tipo de impresora e configuração da porta de comunicação paralela (LPT1, LPT2 ou LPT3)
         */
        /// <summary>
        /// Método que incializa a impressora
        /// </summary>
        private void inicializaImpressora()
        {
            this.validaArquivoConfiguracao();

            this.iRetorno = MP2032.ConfiguraModeloImpressora(TIPOIMPRESSORA);
            if (iRetorno <= 0)//erro na configuração do modelo da impressora
            {
                this.traceMessage("Erro ao configurar o modelo de impressora.");
                this.fechaApp();
            }
            
            this.iRetorno = 0;
            this.iRetorno = MP2032.IniciaPorta(sPorta);
            if (iRetorno <= 0)//erro na configuração na porta de comunicação da impressora
            {
                this.traceMessage("Problemas ao abrir a porta de comunicação.\nVerifique o arquivo de configuração.");
                this.fechaApp();
            }
        }

        #endregion

        #region Manipulação do arquivo de configuração (config.txt)

        /// <summary>
        /// Método que valida os dados do arquivo de configuração (config.txt)
        /// </summary>
        private void validaArquivoConfiguracao()
        {
            try
            {
                if (!File.Exists(this.CAMINHOARQUIVOCONFIGURACAO))//verifica a existência do arquivo de configuração no patch definido
                {
                    this.atualizarArquivoConfig(false);//o arquivo não existe. Portanto, não há atualização a fazer pois ele deve ser criado do zero.
                }
                else//existe o arquivo de configuração
                {
                    string[] lines = File.ReadAllLines(this.CAMINHOARQUIVOCONFIGURACAO);//le todo as linhas do arquivo de configuração (config.txt)
                    if (lines.Length >= this.LINHACONFIGURACAO)//arquivo possui todas as linhas de configuração. Foi utilizado "maior ou igual" pois as vezes a pessoa clica enter criando uma quarta ou quinta linha no arquivo que serão ignoradas.
                    {
                        for (int i = 0; i < this.LINHACONFIGURACAO; i++)
                        {
                            lines[i] = lines[i].Trim().ToUpper();//retirar espaçamentos do inicio, do fim e colocar tudo em maiúsculo.
                        }
                        switch (lines[0])//lines[0] é a porta de comunicação
                        {
                            case "LPT1":
                            case "LPT2":
                            case "LPT3":
                                this.sPorta = lines[0];
                                break;
                            default:
                                exibeMensagem(
                                    string.Format("Caminho: {0}\nInforme, na linha 01, a porta de comunicação com a impressora: LPT1, LPT2 ou LPT3", this.CAMINHOARQUIVOCONFIGURACAO)
                                    );
                                    this.fechaApp();
                                    break;
                        }
                        if (lines[1].Length >= this.CARACTERMINIMOSETOR && lines[1].Length <= this.calculaTamanhoMaximoStringSetor())//lines[1] é o setor que irá protocolar o documento.
                        {
                            this.sSetor = this.trocarCaracteresEspecias(lines[1]);//substituir os caracteres especiais por alfa numéricos
                        }
                        else
                        {
                            exibeMensagem(
                                string.Format("Caminho: {0}\nInforme, na linha 02, o nome do setor. Precisa ser entre {1} e {2} caracteres e não deve utilizar caracteres especiais.",
                                this.CAMINHOARQUIVOCONFIGURACAO,
                                this.CARACTERMINIMOSETOR,
                                (this.calculaTamanhoMaximoStringSetor())));
                            this.fechaApp();
                        }
                        if (
                            (!int.TryParse(lines[2], out this.iTotalAutenticacao)) ||
                            this.iTotalAutenticacao <= 0
                            )
                        {
                            this.exibeMensagem(
                                string.Format("Caminho: {0}\nNa linha 03, o número do último documento autenticado está com problemas.", this.CAMINHOARQUIVOCONFIGURACAO)
                                );
                            this.fechaApp();
                        }
                        else
                        {
                            this.reiniciarContadorMaxAutenticacao();
                        }
                    }
                    else
                    {
                        this.atualizarArquivoConfig(false);//false significa não atualizar e sim criar o arquivo do zero
                    }
                }
            }
            catch (ObjectDisposedException erro)
            {
                this.traceMessage(erro.Message);
            }
            catch (DirectoryNotFoundException erro)
            {
                this.traceMessage(erro.Message);
            }
            catch (PathTooLongException erro)
            {
                this.traceMessage(erro.Message);
            }
            catch (IOException erro)
            {
                this.traceMessage(erro.Message);
            }
            catch (NotSupportedException erro)
            {
                this.traceMessage(erro.Message);
            }
            catch (UnauthorizedAccessException erro)
            {
                this.traceMessage(erro.Message);
            }
            catch (ArgumentNullException erro)
            {
                this.traceMessage(erro.Message);
            }
            catch (ArgumentException erro)
            {
                this.traceMessage(erro.Message);
            }            
        }

        private void atualizarArquivoConfig(bool atualizar)
        {
            using (FileStream fs = File.Create(this.CAMINHOARQUIVOCONFIGURACAO))
            {
                using (StreamWriter writer = new StreamWriter(fs))
                {
                    if (atualizar)
                    {
                        writer.WriteLine(this.sPorta);
                        writer.WriteLine(this.sSetor);
                        writer.WriteLine(this.iTotalAutenticacao);
                    }
                    else
                    {
                        writer.WriteLine("Escolher a PORTA e apagar a linha TODA: LPT1, LPT2 ou LPT3");
                        writer.WriteLine(string.Format("Escrever o LOCAL a ser exibido no recibo e apagar o resto da linha. Não utilizar caracteres especiais e possuir entre {0} e {1} caracteres nessa linha. Ex.: 1A VARA CRIMINAL", this.CARACTERMINIMOSETOR, this.calculaTamanhoMaximoStringSetor()));
                        writer.WriteLine(string.Format("Definir o valor do próximo documento a ser autenticado e apagar toda a linha. Entre 1 e {0}", this.MAXIMOAUTENTICACAO));
                        this.exibeMensagem(string.Concat("Arquivo de configuração foi criado no caminho: ", this.CAMINHOARQUIVOCONFIGURACAO, "\nFavor configurá-lo."));
                        this.fechaApp();
                    }
                }
            }            
        }

        #endregion

        #region manipulacao do formulário - pré-autenticação

        /*
         * Define-se a variável global com o número de vias e muda-se o texto do botão autenticar
         */
        private void rb_CheckedChanged(object sender, EventArgs e)
        {
            this.setNumeroVias();
            this.mudarTextoBotaoAutenticar();
        }

        public void setNumeroVias()
        {
            foreach(Control control in this.gboxNumeroVias.Controls)
            {
                if (control is RadioButton)
                {
                    RadioButton radio = control as RadioButton;
                    if (radio.Checked)
                    {
                        this.iNumeroVias = int.Parse(radio.Text);//converte o texto do radio button em inteiro e atribui ao numero de vias
                        break;
                    }
                }
            }
        }
        #endregion

        #region Autenticação do Documento

        private void btnAutenticar_Click(object sender, EventArgs e)
        {
            this.iRetorno = 0;
            this.iRetorno = MP2032.Le_Status();
            switch (this.iRetorno)
            {
                case 144://On-Line
                    {
                        this.prepararStringAutenticacao();
                        this.iRetorno = 0;
                        this.iRetorno = MP2032.AutenticaDoc(this.sAutenticacao, 50000);
                        if (this.iRetorno <= 0)//documento foi inserido depois do tempo definido
                        {
                            this.exibeMensagem("Time-Out. O documento não foi inserido no tempo determinado.\nPosicione o documento e tente novamente.");
                        }
                        else
                        {
                            if (this.iContadorViasAutenticadas == this.iNumeroVias)
                            {
                                this.iContadorViasAutenticadas = 1;
                                this.iTotalAutenticacao++;
                                this.atualizarArquivoConfig(true);
                                this.reiniciarContadorMaxAutenticacao();
                                this.mudarTextoBotaoAutenticar();
                                this.gboxNumeroVias.Enabled = true;
                            }
                            else
                            {
                                this.gboxNumeroVias.Enabled = false;
                                this.iContadorViasAutenticadas++;
                                this.mudarTextoBotaoAutenticar();
                            }
                        }
                    }
                    break;
                case 79://OFF-Line
                    {
                        this.exibeMensagem("Impressora OFF-Line.");
                    }
                    break;
                case 0://Desligado ou Cabo Desconectado
                    {
                        this.exibeMensagem("Impressora Desligada ou Cabo Desconectado.");
                    }
                    break;
                case 40://Fim do papel
                    {
                        this.exibeMensagem("Impressora sem Papel.");
                    }
                    break;
                default:
                    {
                        this.traceMessage("Status da Impressora Desconhecido.");
                    }
                    break;
            }
        }

        #endregion

        #region validações, comportamentos e mensagens diversas

        /*
         * Método que recebe como parâmetro a mensagem que será exibida com o erro e indica dados para depuração do problema.
         */
        public void traceMessage(string mensagem,
                [CallerMemberName] string memberName = "",
                [CallerFilePath] string sourceFilePath = "",
                [CallerLineNumber] int sourceLineNumber = 0)
        {
            StringBuilder sb = new StringBuilder(mensagem);
            sb.AppendLine();
            sb.AppendLine("Dados técnicos do erro:");
            sb.AppendLine();
            sb.AppendFormat("Método: {0}", memberName);
            sb.AppendLine();
            sb.AppendFormat("Arquivo: {0}", sourceFilePath);
            sb.AppendLine();
            sb.AppendFormat("Linha: {0}", sourceLineNumber);
            MessageBox.Show(sb.ToString(), "Entre em contato com o suporte.", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        public void exibeMensagem(string mensagem)
        {
            MessageBox.Show(mensagem, "Informação", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        public void fechaApp()
        {
            Application.Exit();
        }

        private void frmPrincipal_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.fechaApp();
        }

        private string trocarCaracteresEspecias(String texto)
        {
            string[] acentos = new string[] { "ç", "Ç", "á", "é", "í", "ó", "ú", "ý", "Á", "É", "Í", "Ó", "Ú", "Ý", "à", "è", "ì", "ò", "ù", "À", "È", "Ì", "Ò", "Ù", "ã", "õ", "ñ", "ä", "ë", "ï", "ö", "ü", "ÿ", "Ä", "Ë", "Ï", "Ö", "Ü", "Ã", "Õ", "Ñ", "â", "ê", "î", "ô", "û", "Â", "Ê", "Î", "Ô", "Û", "ª", "º"};
            string[] semAcento = new string[] { "c", "C", "a", "e", "i", "o", "u", "y", "A", "E", "I", "O", "U", "Y", "a", "e", "i", "o", "u", "A", "E", "I", "O", "U", "a", "o", "n", "a", "e", "i", "o", "u", "y", "A", "E", "I", "O", "U", "A", "O", "N", "a", "e", "i", "o", "u", "A", "E", "I", "O", "U", "a", "o"};

            for (int i = 0; i< acentos.Length; i++)
            {
                texto = texto.Replace(acentos[i], semAcento[i]);
            }
            return texto;
        }
        
        private void mudarTextoBotaoAutenticar()
        {
            btnAutenticar.Text = string.Format("Protocolar via {0} de {1}", 
                this.iContadorViasAutenticadas, 
                this.iNumeroVias);
        }

       private void prepararStringAutenticacao()
        {
            this.sAutenticacao = string.Format("JFES {0} {1} {2} {3}/{4}",
                this.sSetor,
                DateTime.Now.ToString().Substring(0, 16),
                this.iTotalAutenticacao.ToString(string.Concat("D", this.MAXIMOAUTENTICACAO.ToString().Length)),//define quantos zeros aparecerão no numero de acordo com o número maximo de autenticação definido para passar como parâmetro no formato: D#, onde # é um número que representa a quantidade de números no valor a ser impresso
                this.iContadorViasAutenticadas,
                this.iNumeroVias);
        }

       public void reiniciarContadorMaxAutenticacao()
       {
           if (this.iTotalAutenticacao > this.MAXIMOAUTENTICACAO)
           {
               this.iTotalAutenticacao = 1;
           }
       }

        public int calculaTamanhoMaximoStringSetor()
       {
           return (this.COLUNAIMPRESSAO - (this.CARACTERUSADOSISTEMA + this.MAXIMOAUTENTICACAO.ToString().Length));
       }

        #endregion
    }
}
